查看原文
其他

NDK探究之旅《十一》—C代码调用Java代码之项目实战

2017-06-01 于亚豪 终端研发部



前言介绍

C代码调用Java代码步骤及其注意事项

博客地址:


正文

C代码调用Java代码步骤及其注意事项

今天我们来探究一下C语言是如何调用Java代码的

  • 我们先来看一下Java代码是如何调用C代码的

那C代码是如何调用Java代码的呢?

C调用Java中的方法

其实就是从java中调用c,从C中调用java的方法,可以看做是一个回调

Java中没有参数的方法

1.在java中写一个本地方法

public native void methodInC();

2.使用命令提示符进入到项目下的bin/classes目录下,使用javah 类的全路径 命令得到本地方法的头文件

3.在项目上点右键-->Android Tools-->Add Native Support,给我们的C代码起一个名字,点确定,会自动生成一个jni的目录和c代码(一般生成的是.cpp,把后缀改为.c)

4.自动生成的Android.mk文件中的编译源文件是.cpp,改为c(如果第三步没有改,则不需要改)

#指定编译的文件夹  指定当前文件目录 LOCAL_PATH := $(call my-dir) #编译器会定义很多的临时变量,中间变量,最好清空所有的中间变量。 include $(CLEAR_VARS) #编译出来模块的名称(编译后的名字,也就是存放于libs目录下的名字,不带扩展名) LOCAL_MODULE    := hello # 编译的源代码的名称(要编译的c代码的名称,带扩展名) LOCAL_SRC_FILES := hello.c         #编译一个动态库 include $(BUILD_SHARED_LIBRARY)

5.如果需要在除arm处理器的其它机型上使用,需要在jni目录下创建一个Application.mk文件(如果只是在arm下使用,可以跳过此步骤),需要哪一个处理器,就配置哪一个,不要配置太多,因为生成的文件会很大

APP_ABI := arm x86 mips

6.把ndk安装的目录下的android-ndk-r9b\platforms\android-16\arch-arm\usr\include/jni.h复制到项目的jni目录下(平台就现在版本没有任何区别,哪一个版本下的都可以) 7.写C代码

#include <jni.h> #include "stdio.h" #include "com_itheima_calljava_DataProvider.h" /** * 用C代码调用java代码中的方法,去发送一条短信 */ JNIEXPORT void JNICALL Java_com_demo_calljava_DataProvider_methodInC  (JNIEnv* env, jobject obj){    //第一步:找到要调用的方法的class字节码文件        jclass clazz = (*env)->FindClass(env,"com/itheima/calljava/DataProvider");    //第二步:找到这个类中的方法.参数依次为:JVM虚拟机环境、class对象、方法名、方法签名(返回值+方法的形参)        jmethodID mid = (*env)->GetMethodID(env,clazz,"methodInjava","()V");    //第三步:调用这个方法        (*env)->CallVoidMethod(env,obj,mid); }

8.在要调用本地方法的类中使用静态代码块的方式加载生成的c代码库

static{    //这个名字是在Android.mk中的LOCAL_MODULE的值,不带前缀,不带扩展名    System.loadLibrary("hello"); }

9.只要在java中调用本地方法methodInC即可
注: Java中的被C调用的方法

/** * 让C调用此方法,发送一条短信 */ public void methodInjava(){    Log.i(TAG,"-------------------------");    //创建短信管理对象    SmsManager smsManager = SmsManager.getDefault();    //发送一条文本短信    smsManager.sendTextMessage("5556", null, "hello 5556", null, null); }

如何去获取方法的签名

Javap -s com.包名.类名 如:

Demo相关示例:

C语言调用Java中的方法去Toast吐司和Log日志打印

相关代码:

public class ServiceProvider {    public  void methodInJavaSource(){        System.out.print("-------->我来了");        LogUtil.e("yuyahao","-------->我来了");        for (int i = 0; i < 20; i++) {            GetToast.useString(MyApplication.getInstance(),"C代码来调用我啦啦啦..."+i);            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    /**     * 调用C代码,让C代码再次调用Java代码     */    public native void callFromCMethod(); }

MainActivity

public class MainActivity extends AppCompatActivity {    static {        System.loadLibrary("serviceData");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        try {           Class service =  getClassLoader().loadClass("jni.yyh.com.mycptojavanative.jni.ServiceProvider");            Method method = service.getMethod("methodInJavaSource",new Class []{});            method.invoke(service.newInstance(),new  Object[]{});            //调用其方法        } catch (Exception e) {            e.printStackTrace();        }        findViewById(R.id.btn_onclick).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                ServiceProvider serviceProvider = new ServiceProvider();                serviceProvider.callFromCMethod();            }        });    } }

主要的就是C代码:

#include <stdio.h> #include"jni_yyh_com_mycptojavanative_jni_ServiceProvider.h" #include <android/log.h> #include"jni.h" #define LOG_TAG "System.out.c" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) //java中调用c代码,直接调用native方法即可。 JNIEXPORT void  Java_jni_yyh_com_mycptojavanative_jni_ServiceProvider_callFromCMethod(JNIEnv * env, jobject obj){    char * pageName = "jni/yyh/com/mycptojavanative/jni/ServiceProvider";  //第一步,首先找到Java 中的class对象  jclass clzz = (*env)->FindClass(env,pageName);  //第二步,找到class里面的一个方法  jmethodID methodid = (*env)->GetMethodID(env,clzz,"methodInJavaSource","()V");    int ss = 0;    LOGD("pagename=%d");    LOGI("pagename=%s",pageName);  //第三步调用clas里面的一个方法  (*env)->CallVoidMethod(env,obj,methodid); }

相关Demo下载地址:

C语言调用Java方法进行Toast吐司和Log日志进行打印

博客地址:

NDK探究之旅:

NDK探究之旅《一》——对jni和NDK的认识

NDK探究之旅《二》——C语言的基本认识

NDK探究之旅《三》—C语言的输入输出函数

NDK探究之旅《四》——指针的强化理解

NDK探究之旅《五》——指针和数组之间的关系

NDK探究之旅《六》—函数的指针、结构体、枚举、宏定义

NDK探究之旅《七》—函数指针,宏定义的优缺点及应用场景

NDK探究之旅《八》——jni的开发流程规范及环境配置

NDK探究之旅《九》——jni开发中常见的错误及其注意事项

NDK探究之旅《十》——ndk项目实战之Androidstudio开发经验总结



让心,在阳光下学会舞蹈

让灵魂,在痛苦中学会微笑

—终端研发部—



如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809   

微信公众号:终端研发部


            

Hello,伙伴们





您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存